﻿#region License and copyright notice
// Original code by Guillaume Leparmentier, licensed under The Code Project Open License (CPOL)
#endregion

namespace CAF.Infrastructure.Core.ImageLibrary.ColorSpace {
    using System;
    using System.Drawing;

    /// <summary>
    /// Provides methods to convert from a color space to an other.
    /// </summary>
    public sealed class ColorSpaceHelper {
        private ColorSpaceHelper() {}

        #region Color processing

        /// <summary>
        /// Gets the "distance" between two colors.
        /// RGB colors must be normalized (eg. values in [0.0, 1.0]).
        /// </summary>
        /// <param name="r1">First color red component.</param>
        /// <param name="g1">First color green component.</param>
        /// <param name="b1">First color blue component.</param>
        /// <param name="r2">Second color red component.</param>
        /// <param name="g2">Second color green component.</param>
        /// <param name="b2">Second color blue component.</param>
        public static double GetColorDistance(double r1, double g1, double b1, double r2, double g2, double b2) {
            double a = r2 - r1;
            double b = g2 - g1;
            double c = b2 - b1;

            return Math.Sqrt(a*a + b*b + c*c);
        }


        /// <summary>
        /// Gets the "distance" between two colors.
        /// RGB colors must be normalized (eg. values in [0.0, 1.0]).
        /// </summary>
        /// <param name="color1">First color [r,g,b]</param>
        /// <param name="color2">Second color [r,g,b]</param>
        public static double GetColorDistance(double[] color1, double[] color2) {
            return GetColorDistance(color1[0], color1[1], color1[2], color2[0], color2[1], color2[2]);
        }


        /// <summary>
        /// Gets the "distance" between two colors.
        /// </summary>
        /// <param name="color1">First color.</param>
        /// <param name="color2">Second color.</param>
        public static double GetColorDistance(Color c1, Color c2) {
            double[] rgb1 = new double[] {
                (double)c1.R/255.0,
                (double)c1.G/255.0,
                (double)c1.B/255.0
            };

            double[] rgb2 = new double[] {
                (double)c2.R/255.0,
                (double)c2.G/255.0,
                (double)c2.B/255.0
            };

            return GetColorDistance(rgb1[0], rgb1[1], rgb1[2], rgb2[0], rgb2[1], rgb2[2]);
        }


        #endregion

        #region Light Spectrum processing

        /// <summary>
        /// Gets visible colors (color wheel).
        /// </summary>
        /// <param name="alpha">
        /// The alpha value used for each colors.
        /// </param>
        public static Color[] GetWheelColors(int alpha) {
            Color temp;
            int colorCount = 6*256;
            Color[] colors = new Color[colorCount];

            for (int i = 0; i < colorCount; i++) {
                temp = HSBtoColor((int)((double)(i*255.0)/colorCount), 255, 255);
                colors[i] = Color.FromArgb(alpha, temp.R, temp.G, temp.B);
            }

            return colors;
        }


        /// <summary>
        /// Gets visible spectrum colors.
        /// </summary>
        /// <param name="alpha">The alpha value used for each colors.</param>
        public static Color[] GetSpectrumColors(int alpha) {
            Color[] colors = new Color[256*6];
            //for(int i=127; i<256; i++) colors[i-127] = Color.FromArgb(alpha, i, 0, 0);
            for (int i = 0; i < 256; i++) colors[i] = Color.FromArgb(alpha, 255, i, 0);
            for (int i = 0; i < 256; i++) colors[i + (256)] = Color.FromArgb(alpha, 255 - i, 255, 0);
            for (int i = 0; i < 256; i++) colors[i + (256*2)] = Color.FromArgb(alpha, 0, 255, i);
            for (int i = 0; i < 256; i++) colors[i + (256*3)] = Color.FromArgb(alpha, 0, 255 - i, 255);
            for (int i = 0; i < 256; i++) colors[i + (256*4)] = Color.FromArgb(alpha, i, 0, 255);
            for (int i = 0; i < 256; i++) colors[i + (256*5)] = Color.FromArgb(alpha, 255, 0, 255 - i);
            //for(int i=0; i<128; i++) colors[i+(128+256*6)] = Color.FromArgb(alpha, 255-i, 0, 0);

            return colors;
        }


        /// <summary>
        /// Gets visible spectrum colors.
        /// </summary>
        public static Color[] GetSpectrumColors() {
            return GetSpectrumColors(255);
        }


        #endregion

        #region Hexa convert

        /// <summary>
        /// Gets the int equivalent for a hexadecimal value.
        /// </summary>
        private static int GetIntFromHex(string strHex) {
            switch (strHex) {
                case ("A"): {
                    return 10;
                }
                case ("B"): {
                    return 11;
                }
                case ("C"): {
                    return 12;
                }
                case ("D"): {
                    return 13;
                }
                case ("E"): {
                    return 14;
                }
                case ("F"): {
                    return 15;
                }
                default: {
                    return int.Parse(strHex);
                }
            }
        }


        /// <summary>
        /// Converts a Hex color to a .net Color.
        /// </summary>
        /// <param name="hexColor">The desired hexadecimal color to convert.</param>
        public static Color HexToColor(string hexColor) {
            string r, g, b;

            if (hexColor != String.Empty) {
                hexColor = hexColor.Trim();
                if (hexColor[0] == '#') hexColor = hexColor.Substring(1, hexColor.Length - 1);

                r = hexColor.Substring(0, 2);
                g = hexColor.Substring(2, 2);
                b = hexColor.Substring(4, 2);

                r = Convert.ToString(16*GetIntFromHex(r.Substring(0, 1)) + GetIntFromHex(r.Substring(1, 1)));
                g = Convert.ToString(16*GetIntFromHex(g.Substring(0, 1)) + GetIntFromHex(g.Substring(1, 1)));
                b = Convert.ToString(16*GetIntFromHex(b.Substring(0, 1)) + GetIntFromHex(b.Substring(1, 1)));

                return Color.FromArgb(Convert.ToInt32(r), Convert.ToInt32(g), Convert.ToInt32(b));
            }

            return Color.Empty;
        }

        /// <summary>
        /// Converts a RGB color format to an hexadecimal color.
        /// </summary>
        /// <param name="r">The Red value.</param>
        /// <param name="g">The Green value.</param>
        /// <param name="b">The Blue value.</param>
        public static string RGBToHex(int r, int g, int b) {
            return String.Format("#{0:x2}{1:x2}{2:x2}", (int)r, (int)g, (int)b);
        }


        /// <summary>
        /// Converts a RGB color format to an hexadecimal color.
        /// </summary>
        /// <param name="r">The color to convert.</param>
        public static string RGBToHex(Color c) {
            return RGBToHex(c.R, c.G, c.B);
        }

        #endregion

        #region HSB convert

        /// <summary>
        /// Converts HSB to RGB.
        /// </summary>
        /// <param name="hsv">The HSB structure to convert.</param>
        public static RGB HSBtoRGB(HSB hsb) {
            double r = 0;
            double g = 0;
            double b = 0;

            if (hsb.Saturation == 0) {
                r = g = b = hsb.Brightness;
            }
            else {
                // the color wheel consists of 6 sectors. Figure out which sector you're in.
                double sectorPos = hsb.Hue/60.0;
                int sectorNumber = (int)(Math.Floor(sectorPos));
                // get the fractional part of the sector
                double fractionalSector = sectorPos - sectorNumber;

                // calculate values for the three axes of the color. 
                double p = hsb.Brightness*(1.0 - hsb.Saturation);
                double q = hsb.Brightness*(1.0 - (hsb.Saturation*fractionalSector));
                double t = hsb.Brightness*(1.0 - (hsb.Saturation*(1 - fractionalSector)));

                // assign the fractional colors to r, g, and b based on the sector the angle is in.
                switch (sectorNumber) {
                    case 0:
                        r = hsb.Brightness;
                        g = t;
                        b = p;
                        break;
                    case 1:
                        r = q;
                        g = hsb.Brightness;
                        b = p;
                        break;
                    case 2:
                        r = p;
                        g = hsb.Brightness;
                        b = t;
                        break;
                    case 3:
                        r = p;
                        g = q;
                        b = hsb.Brightness;
                        break;
                    case 4:
                        r = t;
                        g = p;
                        b = hsb.Brightness;
                        break;
                    case 5:
                        r = hsb.Brightness;
                        g = p;
                        b = q;
                        break;
                }
            }

            return new RGB(
                Convert.ToInt32(Double.Parse(String.Format("{0:0.00}", r*255.0))),
                Convert.ToInt32(Double.Parse(String.Format("{0:0.00}", g*255.0))),
                Convert.ToInt32(Double.Parse(String.Format("{0:0.00}", b*255.0)))
                );
        }

        /// <summary>
        /// Converts HSB to RGB.
        /// </summary>
        /// <param name="H">Hue value.</param>
        /// <param name="S">Saturation value.</param>
        /// <param name="V">Brigthness value.</param>
        public static RGB HSBtoRGB(double h, double s, double b) {
            return HSBtoRGB(new HSB(h, s, b));
        }


        /// <summary>
        /// Converts HSB to Color.
        /// </summary>
        /// <param name="hsv">the HSB structure to convert.</param>
        public static Color HSBtoColor(HSB hsb) {
            RGB rgb = HSBtoRGB(hsb);

            return Color.FromArgb(rgb.Red, rgb.Green, rgb.Blue);
        }

        /// <summary> 
        /// Converts HSB to a .net Color.
        /// </summary>
        /// <param name="h">Hue value (must be between 0 and 360).</param>
        /// <param name="s">Saturation value (must be between 0 and 1).</param>
        /// <param name="b">Brightness value (must be between 0 and 1).</param>
        public static Color HSBtoColor(double h, double s, double b) {
            return HSBtoColor(new HSB(h, s, b));
        }

        /// <summary>
        /// Converts HSB to Color.
        /// </summary>
        /// <param name="h">Hue value.</param>
        /// <param name="s">Saturation value.</param>
        /// <param name="b">Brightness value.</param>
        public static Color HSBtoColor(int h, int s, int b) {
            double hue = 0, sat = 0, val = 0;

            // Scale Hue to be between 0 and 360. Saturation and value scale to be between 0 and 1.
            if (h > 360 || s > 1 || b > 1) {
                hue = ((double)h/255.0*360.0)%360.0;
                sat = (double)s/255.0;
                val = (double)b/255.0;
            }

            return HSBtoColor(new HSB(hue, sat, val));
        }


        /// <summary>
        /// Converts HSB to HSL.
        /// </summary>
        public static HSL HSBtoHSL(double h, double s, double b) {
            RGB rgb = HSBtoRGB(new HSB(h, s, b));

            return RGBtoHSL(rgb.Red, rgb.Green, rgb.Blue);
        }

        /// <summary>
        /// Converts HSB to CMYK.
        /// </summary>
        public static CMYK HSBtoCMYK(double h, double s, double b) {
            RGB rgb = HSBtoRGB(new HSB(h, s, b));

            return RGBtoCMYK(rgb.Red, rgb.Green, rgb.Blue);
        }

        /// <summary>
        /// Converts HSB to CMYK.
        /// </summary>
        public static YUV HSBtoYUV(double h, double s, double b) {
            RGB rgb = HSBtoRGB(new HSB(h, s, b));

            return RGBtoYUV(rgb.Red, rgb.Green, rgb.Blue);
        }

        #endregion

        #region HSL convert

        /// <summary>
        /// Converts HSL to RGB.
        /// </summary>
        /// <param name="h">Hue, must be in [0, 360].</param>
        /// <param name="s">Saturation, must be in [0, 1].</param>
        /// <param name="l">Luminance, must be in [0, 1].</param>
        public static RGB HSLtoRGB(double h, double s, double l) {
            if (s == 0) {
                // achromatic color (gray scale)
                return new RGB(
                    Convert.ToInt32(Double.Parse(String.Format("{0:0.00}", l*255.0))),
                    Convert.ToInt32(Double.Parse(String.Format("{0:0.00}", l*255.0))),
                    Convert.ToInt32(Double.Parse(String.Format("{0:0.00}", l*255.0)))
                    );
            }
            else {
                double q = (l < 0.5) ? (l*(1.0 + s)) : (l + s - (l*s));
                double p = (2.0*l) - q;

                double Hk = h/360.0;
                double[] T = new double[3];
                T[0] = Hk + (1.0/3.0); // Tr
                T[1] = Hk; // Tb
                T[2] = Hk - (1.0/3.0); // Tg

                for (int i = 0; i < 3; i++) {
                    if (T[i] < 0) T[i] += 1.0;
                    if (T[i] > 1) T[i] -= 1.0;

                    if ((T[i]*6) < 1) {
                        T[i] = p + ((q - p)*6.0*T[i]);
                    }
                    else if ((T[i]*2.0) < 1) //(1.0/6.0)<=T[i] && T[i]<0.5
                    {
                        T[i] = q;
                    }
                    else if ((T[i]*3.0) < 2) // 0.5<=T[i] && T[i]<(2.0/3.0)
                    {
                        T[i] = p + (q - p)*((2.0/3.0) - T[i])*6.0;
                    }
                    else T[i] = p;
                }

                return new RGB(
                    Convert.ToInt32(Double.Parse(String.Format("{0:0.00}", T[0]*255.0))),
                    Convert.ToInt32(Double.Parse(String.Format("{0:0.00}", T[1]*255.0))),
                    Convert.ToInt32(Double.Parse(String.Format("{0:0.00}", T[2]*255.0)))
                    );
            }
        }

        /// <summary>
        /// Converts HSL to RGB.
        /// </summary>
        /// <param name="hsl">The HSL structure to convert.</param>
        public static RGB HSLtoRGB(HSL hsl) {
            return HSLtoRGB(hsl.Hue, hsl.Saturation, hsl.Luminance);
        }


        /// <summary>
        /// Converts HSL to .net Color.
        /// </summary>
        /// <param name="hsl">The HSL structure to convert.</param>
        public static Color HSLtoColor(double h, double s, double l) {
            RGB rgb = HSLtoRGB(h, s, l);

            return Color.FromArgb(rgb.Red, rgb.Green, rgb.Blue);
        }

        /// <summary>
        /// Converts HSL to .net Color.
        /// </summary>
        /// <param name="hsl">The HSL structure to convert.</param>
        public static Color HSLtoColor(HSL hsl) {
            return HSLtoColor(hsl.Hue, hsl.Saturation, hsl.Luminance);
        }


        /// <summary>
        /// Converts HSL to HSB.
        /// </summary>
        public static HSB HSLtoHSB(double h, double s, double l) {
            RGB rgb = HSLtoRGB(h, s, l);

            return RGBtoHSB(rgb.Red, rgb.Green, rgb.Blue);
        }

        /// <summary>
        /// Converts HSL to CMYK.
        /// </summary>
        public static CMYK HSLtoCMYK(double h, double s, double l) {
            RGB rgb = HSLtoRGB(h, s, l);

            return RGBtoCMYK(rgb.Red, rgb.Green, rgb.Blue);
        }

        /// <summary>
        /// Converts HSL to YUV.
        /// </summary>
        public static YUV HSLtoYUV(double h, double s, double l) {
            RGB rgb = HSLtoRGB(h, s, l);

            return RGBtoYUV(rgb.Red, rgb.Green, rgb.Blue);
        }

        #endregion

        #region RGB convert

        /// <summary> 
        /// Converts RGB to HSL.
        /// </summary>
        /// <param name="red">Red value, must be in [0,255].</param>
        /// <param name="green">Green value, must be in [0,255].</param>
        /// <param name="blue">Blue value, must be in [0,255].</param>
        public static HSL RGBtoHSL(int red, int green, int blue) {
            double h = 0, s = 0, l = 0;

            // normalizes red-green-blue values
            double nRed = (double)red/255.0;
            double nGreen = (double)green/255.0;
            double nBlue = (double)blue/255.0;

            double max = Math.Max(nRed, Math.Max(nGreen, nBlue));
            double min = Math.Min(nRed, Math.Min(nGreen, nBlue));

            // hue
            if (max == min) {
                h = 0; // undefined
            }
            else if (max == nRed && nGreen >= nBlue) {
                h = 60.0*(nGreen - nBlue)/(max - min);
            }
            else if (max == nRed && nGreen < nBlue) {
                h = 60.0*(nGreen - nBlue)/(max - min) + 360.0;
            }
            else if (max == nGreen) {
                h = 60.0*(nBlue - nRed)/(max - min) + 120.0;
            }
            else if (max == nBlue) {
                h = 60.0*(nRed - nGreen)/(max - min) + 240.0;
            }

            // luminance
            l = (max + min)/2.0;

            // saturation
            if (l == 0 || max == min) {
                s = 0;
            }
            else if (0 < l && l <= 0.5) {
                s = (max - min)/(max + min);
            }
            else if (l > 0.5) {
                s = (max - min)/(2 - (max + min)); //(max-min > 0)?
            }

            return new HSL(
                Double.Parse(String.Format("{0:0.##}", h)),
                Double.Parse(String.Format("{0:0.##}", s)),
                Double.Parse(String.Format("{0:0.##}", l))
                );
        }

        /// <summary> 
        /// Converts RGB to HSL.
        /// </summary>
        public static HSL RGBtoHSL(RGB rgb) {
            return RGBtoHSL(rgb.Red, rgb.Green, rgb.Blue);
        }

        /// <summary> 
        /// Converts Color to HSL.
        /// </summary>
        public static HSL RGBtoHSL(Color c) {
            return RGBtoHSL(c.R, c.G, c.B);
        }


        /// <summary> 
        /// Converts RGB to HSB.
        /// </summary> 
        public static HSB RGBtoHSB(int red, int green, int blue) {
            double r = ((double)red/255.0);
            double g = ((double)green/255.0);
            double b = ((double)blue/255.0);

            double max = Math.Max(r, Math.Max(g, b));
            double min = Math.Min(r, Math.Min(g, b));

            double h = 0.0;
            if (max == r && g >= b) {
                if (max - min == 0) h = 0.0;
                else h = 60*(g - b)/(max - min);
            }
            else if (max == r && g < b) {
                h = 60*(g - b)/(max - min) + 360;
            }
            else if (max == g) {
                h = 60*(b - r)/(max - min) + 120;
            }
            else if (max == b) {
                h = 60*(r - g)/(max - min) + 240;
            }

            double s = (max == 0) ? 0.0 : (1.0 - ((double)min/(double)max));

            return new HSB(h, s, (double)max);
        }

        /// <summary> 
        /// Converts RGB to HSB.
        /// </summary> 
        public static HSB RGBtoHSB(RGB rgb) {
            return RGBtoHSB(rgb.Red, rgb.Green, rgb.Blue);
        }

        /// <summary> 
        /// Converts RGB to HSB.
        /// </summary> 
        public static HSB RGBtoHSB(Color c) {
            return RGBtoHSB(c.R, c.G, c.B);
        }


        /// <summary>
        /// Converts RGB to CMYK
        /// </summary>
        /// <param name="red">Red vaue must be in [0, 255].</param>
        /// <param name="green">Green vaue must be in [0, 255].</param>
        /// <param name="blue">Blue vaue must be in [0, 255].</param>
        public static CMYK RGBtoCMYK(int red, int green, int blue) {
            double c = (double)(255 - red)/255;
            double m = (double)(255 - green)/255;
            double y = (double)(255 - blue)/255;

            double min = (double)Math.Min(c, Math.Min(m, y));
            if (min == 1.0) {
                return new CMYK(0, 0, 0, 1);
            }
            else {
                return new CMYK((c - min)/(1 - min), (m - min)/(1 - min), (y - min)/(1 - min), min);
            }
        }

        /// <summary>
        /// Converts RGB to CMYK
        /// </summary>
        public static CMYK RGBtoCMYK(Color c) {
            return RGBtoCMYK(c.R, c.G, c.B);
        }

        /// <summary>
        /// Converts RGB to CMYK
        /// </summary>
        public static CMYK RGBtoCMYK(RGB rgb) {
            return RGBtoCMYK(rgb.Red, rgb.Green, rgb.Blue);
        }


        /// <summary>
        /// Converts RGB to YUV.
        /// </summary>
        /// <param name="red">red must be in [0, 255].</param>
        /// <param name="green">green must be in [0, 255].</param>
        /// <param name="blue">blue must be in [0, 255].</param>
        public static YUV RGBtoYUV(int red, int green, int blue) {
            YUV yuv = new YUV();

            // normalizes red/green/blue values
            double nRed = (double)red/255.0;
            double nGreen = (double)green/255.0;
            double nBlue = (double)blue/255.0;

            // converts
            yuv.Y = 0.299*nRed + 0.587*nGreen + 0.114*nBlue;
            yuv.U = -0.1471376975169300226*nRed - 0.2888623024830699774*nGreen + 0.436*nBlue;
            yuv.V = 0.615*nRed - 0.5149857346647646220*nGreen - 0.1000142653352353780*nBlue;

            return yuv;
        }

        /// <summary>
        /// Converts RGB to YUV.
        /// </summary>
        public static YUV RGBtoYUV(Color c) {
            return RGBtoYUV(c.R, c.G, c.B);
        }

        /// <summary>
        /// Converts RGB to YUV.
        /// </summary>
        public static YUV RGBtoYUV(RGB rgb) {
            return RGBtoYUV(rgb.Red, rgb.Green, rgb.Blue);
        }


        /// <summary>
        /// Converts RGB to CIE XYZ (CIE 1931 color space)
        /// </summary>
        /// <param name="red">Red must be in [0, 255].</param>
        /// <param name="green">Green must be in [0, 255].</param>
        /// <param name="blue">Blue must be in [0, 255].</param>
        public static CIEXYZ RGBtoXYZ(int red, int green, int blue) {
            // normalize red, green, blue values
            double rLinear = (double)red/255.0;
            double gLinear = (double)green/255.0;
            double bLinear = (double)blue/255.0;

            // convert to a sRGB form
            double r = (rLinear > 0.04045) ? Math.Pow((rLinear + 0.055)/(1 + 0.055), 2.2) : (rLinear/12.92);
            double g = (gLinear > 0.04045) ? Math.Pow((gLinear + 0.055)/(1 + 0.055), 2.2) : (gLinear/12.92);
            double b = (bLinear > 0.04045) ? Math.Pow((bLinear + 0.055)/(1 + 0.055), 2.2) : (bLinear/12.92);

            // converts
            return new CIEXYZ(
                (r*0.4124 + g*0.3576 + b*0.1805),
                (r*0.2126 + g*0.7152 + b*0.0722),
                (r*0.0193 + g*0.1192 + b*0.9505)
                );
        }

        /// <summary>
        /// Converts RGB to CIEXYZ.
        /// </summary>
        public static CIEXYZ RGBtoXYZ(RGB rgb) {
            return RGBtoXYZ(rgb.Red, rgb.Green, rgb.Blue);
        }

        /// <summary>
        /// Converts RGB to CIEXYZ.
        /// </summary>
        public static CIEXYZ RGBtoXYZ(Color c) {
            return RGBtoXYZ(c.R, c.G, c.B);
        }


        /// <summary>
        /// Converts RGB to CIELab.
        /// </summary>
        public static CIELab RGBtoLab(int red, int green, int blue) {
            return XYZtoLab(RGBtoXYZ(red, green, blue));
        }

        /// <summary>
        /// Converts RGB to CIELab.
        /// </summary>
        public static CIELab RGBtoLab(RGB rgb) {
            return XYZtoLab(RGBtoXYZ(rgb.Red, rgb.Green, rgb.Blue));
        }

        /// <summary>
        /// Converts RGB to CIELab.
        /// </summary>
        public static CIELab RGBtoLab(System.Drawing.Color color) {
            return XYZtoLab(RGBtoXYZ(color.R, color.G, color.B));
        }


        #endregion

        #region CMYK convert

        /// <summary>
        /// Converts CMYK to RGB.
        /// </summary>
        /// <param name="c">Cyan value (must be between 0 and 1).</param>
        /// <param name="m">Magenta value (must be between 0 and 1).</param>
        /// <param name="y">Yellow value (must be between 0 and 1).</param>
        /// <param name="k">Black value (must be between 0 and 1).</param>
        public static Color CMYKtoColor(float c, float m, float y, float k) {
            return CMYKtoColor((double)c, (double)m, (double)y, (double)k);
        }

        /// <summary>
        /// Converts CMYK to RGB.
        /// </summary>
        /// <param name="c">Cyan value (must be between 0 and 1).</param>
        /// <param name="m">Magenta value (must be between 0 and 1).</param>
        /// <param name="y">Yellow value (must be between 0 and 1).</param>
        /// <param name="k">Black value (must be between 0 and 1).</param>
        public static Color CMYKtoColor(double c, double m, double y, double k) {
            return CMYKtoColor(new CMYK(c, m, y, k));
        }

        /// <summary>
        /// Converts CMYK to RGB.
        /// </summary>
        /// <param name="cmyk"></param>
        public static Color CMYKtoColor(CMYK cmyk) {
            int red = Convert.ToInt32((1 - cmyk.Cyan)*(1 - cmyk.Black)*255);
            int green = Convert.ToInt32((1 - cmyk.Magenta)*(1 - cmyk.Black)*255);
            int blue = Convert.ToInt32((1 - cmyk.Yellow)*(1 - cmyk.Black)*255);

            return Color.FromArgb(red, green, blue);
        }


        /// <summary>
        /// Converts CMYK to RGB.
        /// </summary>
        /// <param name="c">Cyan value (must be between 0 and 1).</param>
        /// <param name="m">Magenta value (must be between 0 and 1).</param>
        /// <param name="y">Yellow value (must be between 0 and 1).</param>
        /// <param name="k">Black value (must be between 0 and 1).</param>
        public static RGB CMYKtoRGB(double c, double m, double y, double k) {
            int red = Convert.ToInt32((1.0 - c)*(1.0 - k)*255.0);
            int green = Convert.ToInt32((1.0 - m)*(1.0 - k)*255.0);
            int blue = Convert.ToInt32((1.0 - y)*(1.0 - k)*255.0);

            return new RGB(red, green, blue);
        }

        /// <summary>
        /// Converts CMYK to RGB.
        /// </summary>
        /// <param name="cmyk"></param>
        public static RGB CMYKtoRGB(CMYK cmyk) {
            return CMYKtoRGB(cmyk.Cyan, cmyk.Magenta, cmyk.Yellow, cmyk.Black);
        }


        /// <summary>
        /// Converts CMYK to HSL.
        /// </summary>
        public static HSL CMYKtoHSL(double c, double m, double y, double k) {
            RGB rgb = CMYKtoRGB(c, m, y, k);

            return RGBtoHSL(rgb.Red, rgb.Green, rgb.Blue);
        }

        /// <summary>
        /// Converts CMYK to HSB.
        /// </summary>
        public static HSB CMYKtoHSB(double c, double m, double y, double k) {
            RGB rgb = CMYKtoRGB(c, m, y, k);

            return RGBtoHSB(rgb.Red, rgb.Green, rgb.Blue);
        }

        /// <summary>
        /// Converts CMYK to YUV.
        /// </summary>
        public static YUV CMYKtoYUV(double c, double m, double y, double k) {
            RGB rgb = CMYKtoRGB(c, m, y, k);

            return RGBtoYUV(rgb.Red, rgb.Green, rgb.Blue);
        }

        #endregion

        #region YUV convert

        /// <summary>
        /// Converts YUV to RGB.
        /// </summary>
        /// <param name="y">Y must be in [0, 1].</param>
        /// <param name="u">U must be in [-0.436, +0.436].</param>
        /// <param name="v">V must be in [-0.615, +0.615].</param>
        public static RGB YUVtoRGB(double y, double u, double v) {
            RGB rgb = new RGB();

            rgb.Red = Convert.ToInt32((y + 1.139837398373983740*v)*255);
            rgb.Green = Convert.ToInt32((y - 0.3946517043589703515*u - 0.5805986066674976801*v)*255);
            rgb.Blue = Convert.ToInt32((y + 2.032110091743119266*u)*255);

            return rgb;
        }

        /// <summary>
        /// Converts YUV to RGB.
        /// </summary>
        public static RGB YUVtoRGB(YUV yuv) {
            return YUVtoRGB(yuv.Y, yuv.U, yuv.V);
        }


        /// <summary>
        /// Converts YUV to a .net Color.
        /// </summary>
        /// <param name="y">Y must be in [0, 1].</param>
        /// <param name="u">U must be in [-0.436, +0.436].</param>
        /// <param name="v">V must be in [-0.615, +0.615].</param>
        public static Color YUVtoColor(double y, double u, double v) {
            RGB rgb = YUVtoRGB(y, u, v);

            return Color.FromArgb(rgb.Red, rgb.Green, rgb.Blue);
        }

        /// <summary>
        /// Converts YUV to a .net Color.
        /// </summary>
        public static Color YUVtoColor(YUV yuv) {
            RGB rgb = YUVtoRGB(yuv);

            return Color.FromArgb(rgb.Red, rgb.Green, rgb.Blue);
        }


        /// <summary>
        /// Converts YUV to HSL.
        /// </summary>
        /// <param name="y">Y must be in [0, 1].</param>
        /// <param name="u">U must be in [-0.436, +0.436].</param>
        /// <param name="v">V must be in [-0.615, +0.615].</param>
        public static HSL YUVtoHSL(double y, double u, double v) {
            RGB rgb = YUVtoRGB(y, u, v);

            return RGBtoHSL(rgb.Red, rgb.Green, rgb.Blue);
        }

        /// <summary>
        /// Converts YUV to HSB.
        /// </summary>
        /// <param name="y">Y must be in [0, 1].</param>
        /// <param name="u">U must be in [-0.436, +0.436].</param>
        /// <param name="v">V must be in [-0.615, +0.615].</param>
        public static HSB YUVtoHSB(double y, double u, double v) {
            RGB rgb = YUVtoRGB(y, u, v);

            return RGBtoHSB(rgb.Red, rgb.Green, rgb.Blue);
        }

        /// <summary>
        /// Converts YUV to CMYK.
        /// </summary>
        /// <param name="y">Y must be in [0, 1].</param>
        /// <param name="u">U must be in [-0.436, +0.436].</param>
        /// <param name="v">V must be in [-0.615, +0.615].</param>
        public static CMYK YUVtoCMYK(double y, double u, double v) {
            RGB rgb = YUVtoRGB(y, u, v);

            return RGBtoCMYK(rgb.Red, rgb.Green, rgb.Blue);
        }

        #endregion

        #region CIE XYZ convert

        /// <summary>
        /// Converts CIEXYZ to RGB structure.
        /// </summary>
        public static RGB XYZtoRGB(double x, double y, double z) {
            double[] Clinear = new double[3];
            Clinear[0] = x*3.2410 - y*1.5374 - z*0.4986; // red
            Clinear[1] = -x*0.9692 + y*1.8760 - z*0.0416; // green
            Clinear[2] = x*0.0556 - y*0.2040 + z*1.0570; // blue

            for (int i = 0; i < 3; i++) {
                Clinear[i] = (Clinear[i] <= 0.0031308) ? 12.92*Clinear[i] : (1 + 0.055)*Math.Pow(Clinear[i], (1.0/2.4)) - 0.055;
            }

            return new RGB(
                Convert.ToInt32(Double.Parse(String.Format("{0:0.00}", Clinear[0]*255.0))),
                Convert.ToInt32(Double.Parse(String.Format("{0:0.00}", Clinear[1]*255.0))),
                Convert.ToInt32(Double.Parse(String.Format("{0:0.00}", Clinear[2]*255.0)))
                );
        }

        /// <summary>
        /// Converts CIEXYZ to RGB structure.
        /// </summary>
        public static RGB XYZtoRGB(CIEXYZ xyz) {
            return XYZtoRGB(xyz.X, xyz.Y, xyz.Z);
        }


        /// <summary>
        /// XYZ to L*a*b* transformation function.
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        private static double Fxyz(double t) {
            return ((t > 0.008856) ? Math.Pow(t, (1.0/3.0)) : (7.787*t + 16.0/116.0));
        }

        /// <summary>
        /// Converts CIEXYZ to CIELab structure.
        /// </summary>
        public static CIELab XYZtoLab(double x, double y, double z) {
            CIELab lab = CIELab.Empty;

            lab.L = 116.0*Fxyz(y/CIEXYZ.D65.Y) - 16;
            lab.A = 500.0*(Fxyz(x/CIEXYZ.D65.X) - Fxyz(y/CIEXYZ.D65.Y));
            lab.B = 200.0*(Fxyz(y/CIEXYZ.D65.Y) - Fxyz(z/CIEXYZ.D65.Z));

            return lab;
        }

        /// <summary>
        /// Converts CIEXYZ to CIELab structure.
        /// </summary>
        public static CIELab XYZtoLab(CIEXYZ xyz) {
            return XYZtoLab(xyz.X, xyz.Y, xyz.Z);
        }


        #endregion

        #region CIE L*a*b* convert

        /// <summary>
        /// Converts CIELab to CIEXYZ.
        /// </summary>
        public static CIEXYZ LabtoXYZ(double l, double a, double b) {
            double theta = 6.0/29.0;

            double fy = (l + 16)/116.0;
            double fx = fy + (a/500.0);
            double fz = fy - (b/200.0);

            return new CIEXYZ(
                (fx > theta) ? CIEXYZ.D65.X*(fx*fx*fx) : (fx - 16.0/116.0)*3*(theta*theta)*CIEXYZ.D65.X,
                (fy > theta) ? CIEXYZ.D65.Y*(fy*fy*fy) : (fy - 16.0/116.0)*3*(theta*theta)*CIEXYZ.D65.Y,
                (fz > theta) ? CIEXYZ.D65.Z*(fz*fz*fz) : (fz - 16.0/116.0)*3*(theta*theta)*CIEXYZ.D65.Z
                );
        }

        /// <summary>
        /// Converts CIELab to CIEXYZ.
        /// </summary>
        public static CIEXYZ LabtoXYZ(CIELab lab) {
            return LabtoXYZ(lab.L, lab.A, lab.B);
        }


        /// <summary>
        /// Converts CIELab to RGB.
        /// </summary>
        public static RGB LabtoRGB(double l, double a, double b) {
            return XYZtoRGB(LabtoXYZ(l, a, b));
        }

        /// <summary>
        /// Converts CIELab to RGB.
        /// </summary>
        public static RGB LabtoRGB(CIELab lab) {
            return XYZtoRGB(LabtoXYZ(lab));
        }


        #endregion
    }

}
